package com.iflytek.jcy.supervise.job.component.httpinterface;

import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

import com.dtflys.forest.annotation.Address;
import com.dtflys.forest.annotation.BaseRequest;
import com.dtflys.forest.annotation.Body;
import com.dtflys.forest.annotation.DataFile;
import com.dtflys.forest.annotation.Get;
import com.dtflys.forest.annotation.GetRequest;
import com.dtflys.forest.annotation.JSONBody;
import com.dtflys.forest.annotation.JSONQuery;
import com.dtflys.forest.annotation.LogEnabled;
import com.dtflys.forest.annotation.Post;
import com.dtflys.forest.annotation.Query;
import com.dtflys.forest.annotation.Request;
import com.dtflys.forest.annotation.Var;
import com.dtflys.forest.callback.OnProgress;
import com.dtflys.forest.converter.json.JacksonEncoder;
import com.dtflys.forest.extensions.DownloadFile;
import com.dtflys.forest.http.ForestRequest;

/**
 * Forest 一个开源的 Java HTTP 客户端框架
 * https://forest.dtflyx.com/docs/
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2021/12/23
 */
// 整个接口下的所有方法请求都默认绑定该根地址
@Address(source = HttpAddressSource.class) // 动态根地址
@BaseRequest(interceptor = {SimpleInterceptor.class})
public interface OuterHttpInterface {

    @Get("http://localhost:8080/")
    String simpleGet();

    /**
     * 整个完整的URL都通过参数传入
     * {0}代表引用第一个参数
     */
    @Get("{0}")
    String send1(String myURL);

    /**
     * 整个完整的URL都通过 @Var 注解修饰的参数动态传入
     */
    @Get("{myURL}")
    String send2(@Var("myURL") String myURL);

    // 通过 @Address 注解绑定根地址
    // host 绑定到第一个参数， port 绑定到第二个参数
    @Post("/data")
    @Address(host = "{0}", port = "{1}")
    ForestRequest<String> sendHostPort(String host, int port);

    // 也是通过 @Address 注解来绑定动态地址来源
    // 每次调用该方法，都可能是不同的根地址
    @Post("/data")
    @Address(source = HttpAddressSource.class)
    ForestRequest<String> sendData();


    /**
     * 使用 @Query 注解，可以直接将该注解修饰的参数动态绑定到请求url中
     * 注解的 value 值即代表它在url的Query部分的参数名
     */
    @Request("http://localhost:8080/abc?id=0")
    String send(@Query("a") String a, @Query("b") String b);

    /*
     * 接受列表参数为URL查询参数
     * http://localhost:8080/abc?id=1&id=2&id=3&id=4
     */
    @Get("http://localhost:8080/abc")
    String send1(@Query("id") List idList);

    @Get("http://localhost:8080/abc")
    String send(@JSONQuery("id") List idList);

    /**
     * 默认body格式为 application/x-www-form-urlencoded，即以表单形式序列化数据
     */
    @Post(url = "http://localhost:8080/user", headers = {"Accept:text/plain"})
    String sendPost(@Body("username") String username, @Body("password") String password);

    /**
     * contentType 属性不设置默认为 application/x-www-form-urlencoded
     * 要以对象作为表达传输项时，其 @Body 注解的 value 名称不能设置
     * BODY:
     * username=foo&password=bar
     */
    @Post(url = "http://localhost:8080/hello/user", headers = {"Accept:text/plain"})
    // @GsonEncoder
    String send(@Body Object user);

    /**
     * 被@JSONBody注解修饰的参数会根据其类型被自定解析为JSON字符串
     * 使用@JSONBody注解时可以省略 contentType = "application/json"属性设置
     * {"username": "foo", "password": "bar"}
     */
    @Post("http://localhost:8080/hello/user")
    @JacksonEncoder
    String helloUser(@JSONBody Object user);

    /**
     * 按键值对分别修饰不同的参数
     * 这时每个参数前的 @JSONBody 注解必须填上 value 属性或 name 属性的值，作为JSON的字段名称
     */
    @Post("http://localhost:8080/hello/user")
    // @FastjsonEncoder
    String helloUser(@JSONBody("username") String username, @JSONBody("password") String password);

    /**
     * 被@JSONBody注解修饰的Map类型参数会被自定解析为JSON字符串
     */
    @Post(url = "http://localhost:8080/hello/user")
    String helloUser(@JSONBody Map<String, Object> user);

    /**
     * 被@JSONBody注解修饰的List类型参数会被自定解析为JSON字符串
     * ["A", "B", "C"]
     */
    @Post(url = "http://localhost:8080/hello/user-names")
    String helloUserNames(@JSONBody List<String> usernames);

    /**
     * 发送Byte数组类型数据
     */
    @Post(url = "/upload/${filename}", contentType = "application/octet-stream")
    String sendByteArryr(@Body byte[] body, @Var("filename") String filename);

    /**
     * 发送File类型数据
     */
    @Post(url = "/upload/${filename}", contentType = "application/octet-stream")
    String sendFile(@Body File file, @Var("filename") String filename);

    /**
     * 发送输入流类型数据
     */
    @Post(url = "/upload/${filename}", contentType = "application/octet-stream")
    String sendInputStream(@Body InputStream inputStream, @Var("filename") String filename);

    /**
     * 用@DataFile注解修饰要上传的参数对象
     * OnProgress参数为监听上传进度的回调函数
     *
     * Map result = myClient.upload("D:\\TestUpload\\xxx.jpg", progress -> {
     *     System.out.println("total bytes: " + progress.getTotalBytes());   // 文件大小
     *     System.out.println("current bytes: " + progress.getCurrentBytes());   // 已上传字节数
     *     System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已上传百分比
     *     if (progress.isDone()) {   // 是否上传完成
     *         System.out.println("--------   Upload Completed!   --------");
     *     }
     * });
     */
    @Post(url = "/upload")
    Map upload(@DataFile("file") String filePath, OnProgress onProgress);

    /**
     * 在方法上加上@DownloadFile注解
     * dir属性表示文件下载到哪个目录
     * filename属性表示文件下载成功后以什么名字保存，如果不填，这默认从URL中取得文件名
     * OnProgress参数为监听上传进度的回调函数
     *
     * File file = myClient.downloadFile("D:\\TestDownload", progress -> {
     *     System.out.println("total bytes: " + progress.getTotalBytes());   // 文件大小
     *     System.out.println("current bytes: " + progress.getCurrentBytes());   // 已下载字节数
     *     System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已下载百分比
     *     if (progress.isDone()) {   // 是否下载完成
     *         System.out.println("--------   Download Completed!   --------");
     *     }
     * });
     */
    @Get(url = "http://localhost:8080/images/xxx.jpg")
    @DownloadFile(dir = "${0}", filename = "${1}")
    File downloadFile(String dir, String filename, OnProgress onProgress);

    /**
     * 返回类型用byte[]，可将下载的文件转换成字节数组
     */
    @GetRequest(url = "http://localhost:8080/images/test-img.jpg")
    @LogEnabled(logResponseContent = false) // 不打印响应体
    byte[] downloadImageToByteArray();

}
